package com.cskaoyan.javase.oop3._2inner._5lambda._1basic;

/**
 * Lambda表达式的简化
 * Lambda表达式能够简化的前提是：
 * 它是功能接口的子类对象，而功能接口中有且只有一个必须要实现的抽象方法！
 * 并且Lambda表达式的简化是不能影响Lambda表达式的语义的。
 *
 * 基于下列Lambda表达式的语法：
 * (形参列表) -> {
 *  // 方法体
 * }
 *
 * 1."(形参列表)"能不能简化呢？
 * 可以的
 * 由于Lambda表达式实现的功能接口中的抽象方法
 * 中形参列表的数据类型是一定的
 * 所以形参列表中的数据类型可以省略
 * 但是形参名 不能省略 因为要在方法体中使用
 *
 * 特殊的
 * 如果功能接口中的抽象方法只有一个参数
 * 那么可以省略小括号
 * 但是如果方法是无参的，不能直接去掉小括号
 *
 * 2."{}"能不能省略？
 * 是可以的
 * 如果Lambda表达式的重写方法体语句仅有一条
 * 那么可以省略大括号
 * 特殊的
 * 当重写的抽象方法具有返回值时，且恰好返回值语句就是那一条语句
 * 那么连带return和大括号可以一起省略。
 *
 * 以上两种简化的方式，其中第一种是很常见的
 * 但是第二种很少见
 * 因为实际开发中很难出现一条语句重写方法
 * 而Lambda表达式想要写的简洁，大括号是必然要省略的
 *
 * 实际上在多数情况下，都不太可能一句话把方法体写完。
 * 多数情况下，Lambda表达式的抽象方法实现都会很复杂，
 * 那这样Lambda表达式就会写的很复杂，这就很难受了。
 *
 * 而Lambda表达式，本质上就是重写了一个抽象方法的子类对象，
 * 所以Java允许Lambda表达式的抽象方法的实现可以直接指向一个已经存在的方法，而不是自己书写实现。
 * 这种语法在Java中称之为"方法引用"！
 *
 * 那么什么样的方法可以作为Lambda表达式的抽象方法的实现呢？
 * 对于以下抽象方法:
 * 返回值类型 方法名(形参列表);
 *
 * 那么就要求已存在的方法：
 * [修饰符列表] 返回值类型 方法名(形参列表){
 *     // 方法体
 * }
 * 1.访问权限修饰符没有过多的要求,但是肯定是要有访问权限的,没有访问权限肯定是不能用的
 * 2.static还是非static没有要求,无非是调用方式不同.
 * 3.返回值类型是有要求
 * 方法重写中,要求子类重写的返回值类型:
 *      a.void和基本数据类型要保持一致
 *      b.引用数据类型
 *          如果抽象方法是返回父类类型
 *          那么这个已实现的方法可以返回子类类型
 *
 * 4.方法名.没有要求,可以随便写.
 * 5.形参列表,必须一模一样.
 * 6.方法体,没有要求,随便写,不写空着都行.
 *
 * 具体的语法，方法引用有两种形式：
 * 1.不省略"->"的写法
 * 语法：
 *    功能接口 引用名 =   (形参列表) -> 已实现方法的方法调用(形参列表)
 * 解释:
 *      已实现方法的方法调用
 *      就是在这个位置,如何调用该方法
 *      static方法,需要类名点调用
 *      成员方法,需要对象调用
 *
 *      左右两边的形参列表必须保持一致,可以使用简化语法
 *
 * 2.省略"->"的写法
 *      功能接口 引用名 = 已存在方法的方法归属者::方法名;
 *      已存在方法的方法归属者怎么判断?
 *      static方法属于类,就写类名
 *      成员方法属于对象,要么写匿名对象,要么写引用
 *
 *  方法引用在Lambda表达式的使用中是最常见的语法
 *
 * 扩展/补充:
 * 方法引用不仅可以指向自己自定义的方法
 * JDK源码以及三方类库中已存在的方法也可以.
 *
 * @since 14:11
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        INoReturnTwoParam ip1 = (int a, int b) -> {
            System.out.println("这两个数的和是" + (a + b));
        };
        ip1.test(10, 20);

        // 形参列表的简化
        INoReturnTwoParam ip2 = (a, b) -> {
            System.out.println("这两个数的和是" + (a + b));
        };
        ip1.test(10, 20);

        INoReturnOneParam ip3 = (a) -> {
            System.out.println("a的值是" + a);
        };
        ip3.test(666);

        // 形参列表的简化
        INoReturnOneParam ip4 = a -> {
            System.out.println("a的值是" + a);
        };
        ip3.test(666);

        INoReturnNoParam ip5 = () -> {
            System.out.println("hello world!");
        };
        ip5.test();

        // 简化大括号
        INoReturnNoParam ip6 = () -> System.out.println("你好世界！");
        ip6.test();

        IHasReturnNoParam ip7 = () -> {
            return 100;
        };
        System.out.println(ip7.test());
        IHasReturnNoParam ip8 = () -> 200;
        System.out.println(ip8.test());

        // 在这里使用方法引用的语法
        INoReturnOneParam ip9 = a -> myTest(a);
        ip9.test(666);

        IHasReturnTwoParam ip10 = (a, b) -> new Demo().myTest(a, b);
        System.out.println(ip10.test(1, 1));

        System.out.println("----------------------");
        IA ia = Demo::myTest;
        System.out.println(ia.test("abc"));

        System.out.println("----------------------");
        IA ia2 = new Demo()::myTest2;
        System.out.println(ia2.test(""));

        // 使用println方法作为Lambda表达式的抽象方法的实现
        // println方法是成员方法,属于对象,属于System.out
        IB ib = System.out::println;
        ib.test("---------------------------");
        ib.test("我们不一样,我高端!");
    }

    public Object myTest2(Object o) {
        return "hello world!";
    }

    // IA的抽象方法实现
    public static String myTest(Object obj) {
        System.out.println(obj);
        return "我是谁啊?";
    }

    // INoReturnOneParam的抽象方法实现
    public static void myTest(int num) {
        System.out.println("-------------------");
        System.out.println("我是一个小天才!" + num);
        System.out.println("-------------------");
    }

    // IHasReturnTwoParam的抽象方法实现
    public int myTest(int num1, int num2) {
        System.out.println("-------------------");
        System.out.println("你是一个大笨蛋!");
        System.out.println("-------------------");
        return num1 + num2;
    }
}

@FunctionalInterface
interface IB {
    void test(String str);
}

@FunctionalInterface
interface IA {
    Object test(Object o);
}

//无返回值无参数的功能接口
@FunctionalInterface
interface INoReturnNoParam {
    void test();
}

//无返回值有一个参数的功能接口
@FunctionalInterface
interface INoReturnOneParam {
    void test(int a);
}

//无返回值两个参数的功能接口
@FunctionalInterface
interface INoReturnTwoParam {
    void test(int a, int b);
}

//有返回值无参数的功能接口
@FunctionalInterface
interface IHasReturnNoParam {
    int test();
}

//有返回值一个参数的功能接口
@FunctionalInterface
interface IHasReturnOneParam {
    int method(int a);
}

//有返回值两个参数的功能接口
@FunctionalInterface
interface IHasReturnTwoParam {
    int test(int a, int b);
}